package Dungeon;

import SquarePackage.Direction;
import SquarePackage.Square;
import be.kuleuven.cs.som.annotate.*;

/**
 * Class for a dungeon.
 * 
 * @invar	All coordinates are positive and smaller than the maximum value.
 * 			| isValidDimension()
 * 
 * @author 	Brecht Reynders en Florian Hendrickx
 * @version 1.0
 */
public abstract class Subdungeon {
	/**
	 * Initializes a new dungeon with the given coordinates for the maximum coordinate
	 * 
	 * @param	maxX
	 * 			The maximum x-coordinate value.
	 * @param	maxY
	 * 			The maximum y-coordinate value.
	 * @param	maxZ
	 * 			The maximum z-coordinate value.
	 * @post	The given coordinates are the coordinates of the maximum dimension.
	 * 			| Dimension(maxX, maxY, maxZ) == new.getMaxDimension() 
	 */
	public Subdungeon(long maxX, long maxY, long maxZ){
		this.maxDimension = new Dimension(maxX, maxY, maxZ);
	}
	/**
	 * Initializes a new dungeon with the given dimension as maximum dimension.
	 * 
	 * @param 	maxDimension
	 * 			The maximum dimension
	 * @post	The given maximum dimension is the new max dimension.
	 * 			| new.getMaxDimension = maxDimension
	 */
	public Subdungeon(Dimension maxDimension){
		this.maxDimension = maxDimension;
	}
	
	/**
	 * Variable registering the maximum position.
	 */
	private Dimension maxDimension;
	
	/**
	 * Variable registering the containing dungeon.
	 */
	private ComposedDungeon containingDungeon;
	
	/**
	 * Return the maximum dimension.
	 */
	@Basic @Raw
	public Dimension getMaxDimension(){
		return maxDimension;
	}
	
	/**
	 * Sets the maximum dimension of the dungeon.
	 * 
	 * @param 	newMaxDimension
	 * 			The new wanted maximum dimension.
	 * @post	The given maximum dimension is the new maximum dimension.
	 * 			| new.maximumDimension == newMaxdimension
	 * @throws 	IllegalArgumentException
	 * 			The given new maximum is not a valid dimension.
	 * 			| !isValidNewMaxDimension(newMaxDimension)
	 */
	@Model
	public void setMaxDimension(Dimension newMaxDimension) throws IllegalArgumentException{
		if(!isValidNewMaxDimension(newMaxDimension))
			throw new IllegalArgumentException("Not a valid maximum dimension");
		maxDimension = newMaxDimension;
	}

	/**
	 * Checks whether the given dimension is a valid new maximum dimension.
	 * 
	 * @param 	dimension
	 * 			The new wanted dimension.
	 * @return	True if and only if the dimension is larger than the previous maximum dimension
	 * 			| result = dimension.getXPosition() >= getMaxDimension().getXPosition() && dimension.getYPosition() >= getMaxDimension().getYPosition() && dimension.getZPosition() >= getMaxDimension().getZPosition()
	 */
	protected boolean isValidNewMaxDimension(Dimension dimension){
		if (dimension == null)
			return false;
		if (dimension.getXPosition() >= getMaxDimension().getXPosition() && dimension.getYPosition() >= getMaxDimension().getYPosition() && dimension.getZPosition() >= getMaxDimension().getZPosition())
			return true;
		return false;
	}
	
	/**
	 * Returns the root dungeon.
	 * 
	 * @return	The root dungeon of the containing dungeon, if it has a root dungeon, else this is the root dungeon.
	 * 			| if(getContainingDungeon() == null)
	 * 			| return this
	 * 			| return getContainingDungeon().getRootDungeon()
	 */
	public ComposedDungeon getRootDungeon(){
		if(hasContainingDungeon())
			return (ComposedDungeon) this;
		return getContainingDungeon().getRootDungeon();
		}
	
	/**
	 * Returns the containing dungeon.
	 */
	public ComposedDungeon getContainingDungeon(){
		return containingDungeon;
		}
	
	/**
	 * Checks whether this dungeon has a containing dungeon.
	 * 
	 * @return	True if and only if this dungeon has a containing dungeon.
	 * 			| result = containingDungeon != null
	 */
	private boolean hasContainingDungeon(){
		return containingDungeon != null;
	}
	
	/**
	 * Returns the dimension of the given square in this dungeon.
	 * 
	 * @param 	square
	 * 			The square of which the dimension should be returned.
	 * @return	The dimension in this dungeon where square is located.
	 */
	protected abstract Dimension getDimensionOf(Square square);
	
	/**
	 * Returns the square that is at the given dimension.
	 * 
	 * @param 	dimension
	 * 			The dimension of which the square should be returned.
	 * @return	The square at the given dimension.
	 */
	public abstract Square getSquareAt(Dimension dimension);
	
	/**
	 * Remove the given square at the given dimension.
	 * 
	 * @param	dimension
	 * 			The given dimension where the square must be removed.
	 * @post	The square at the given position isn't a part of the dungeon anymore.
	 * 		  	| getSquareAt(dimension)=null
	 */
	@Model
	public abstract void removeSquareAt(Dimension dimension);
	
	/**
	 * Closes a door in the given square in the given direction.
	 * 
	 * @param 	square
	 * 			The square which contains a door.
	 * @param 	direction
	 * 			The direction in the given square where the door stands.
	 * @post	The rooms on both sides of the door get separated if needed.
	 * @throws 	IllegalArgumentException
	 * 			The given square doesn't exist in this room.
	 * 			| getDimensionOf(square) == null
	 * @throws 	IllegalArgumentException
	 * 			There is no door in the given direction or the square isn't part of this dungeon.
	 * 			| !isDoor(square, direction)
	 */
	@Model
	public abstract void closeDoor(Square square, Direction direction);
	
	/**
	 * Checker whether the given square has been used or not.
	 * 
	 * @param 	square
	 * 			The square to check.
	 * @return	True if and only if the given square has already been used in this dungeon.
	 */
	public abstract boolean hasBeenUsed(Square square);
	
	/**
	 * Add the given square on the given dimension.
	 * 
	 * @param 	square
	 * 			The square to add.
	 * @param 	dimension
	 * 			The dimension where the square should be added.
	 * @post	The square is added at the given dimension.
	 * 			| getAllSquares().contains(square)
	 * 			| getAllDimensions().contains(dimension)
	 * @throws 	IllegalArgumentException
	 * 			One of the arguments is not a valid argument.
	 * 			| !isValidDimension()||!isValidSquare()
	 */
	@Model
	public abstract void addSquare(Square square, Dimension dimension);

}
